package me.suntao.javaio.client.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * Created by suntao on 2017/6/5.
 */
public class NioClient {
  /**
   * 服务端口号
   */
  private static final int DEFAULT_PORT = 28000;
  /**
   * 模拟客户端数量
   */
  private static final int CLIENT_COUNT = 20;

  public static void main(String[] args) throws IOException {
    CyclicBarrier cyclicBarrier = new CyclicBarrier(CLIENT_COUNT);
    for (int i=0;i<CLIENT_COUNT;i++){
      new Thread(new NioClient.ClientThread(cyclicBarrier)).start();
    }
  }

  static class ClientThread implements Runnable {

    final CyclicBarrier barrier;
    private Selector selector;

    ClientThread(CyclicBarrier barrier) throws IOException {
      this.barrier = barrier;
    }

    public void run() {
      SocketChannel channel = null;
      try {
        barrier.await();
        //获得一个select通道
        channel = SocketChannel.open();
        //设为非阻塞
        channel.configureBlocking(false);
        //通道管理器
        this.selector = Selector.open();
        //客户端连接服务器，其实这个方法并没有实现连接，需要在listen()方法中调用channel.finishConnect()才能完成连接
        channel.connect(new InetSocketAddress("127.0.0.1", DEFAULT_PORT));
        //通道管理器和通道绑定，并为该通道绑定OP_CONNECT事件
        channel.register(selector, SelectionKey.OP_CONNECT);
        listen();


      } catch (InterruptedException e) {
        e.printStackTrace();
      } catch (BrokenBarrierException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      } finally {
        if (channel != null) {
          try {
            channel.close();
          } catch (IOException e) {
            e.printStackTrace();
          }
        }
      }
    }

    /**
     * 轮询的方式监听selector上是否有需要处理的事件
     */
    private void listen() throws IOException {
      do {
        selector.select();
        Iterator ite = this.selector.selectedKeys().iterator();
        while (ite.hasNext()) {
          SelectionKey key = (SelectionKey) ite.next();
          //删除已选的key
          ite.remove();
          SocketChannel channel = (SocketChannel) key.channel();
          //链接事件发生
          if (key.isConnectable()) {
            //如果正在链接，则完成连接
            if (channel.isConnectionPending()) {
              channel.finishConnect();
            }
            //设置为非堵塞
            channel.configureBlocking(false);
            //向服务器发生消息
            String msg = String
              .format("clientname:%s, senttime:%s", Thread.currentThread().getName(), System.currentTimeMillis());
            channel.write(ByteBuffer.wrap(msg.getBytes("UTF-8")));

            //链接成功之后为了读到服务器的消息，需要注册读事件
            channel.register(this.selector, SelectionKey.OP_READ);
            ByteBuffer buffer = ByteBuffer.allocate(200);
            key.attach(buffer);
          } else if (key.isReadable()) {
            //开始处理读
            ByteBuffer output = (ByteBuffer) key.attachment();
            int count = channel.read(output);
            if (count > 0) {
              output.flip();
              byte[] data = output.array();
              String msg = new String(data, "UTF-8");
              System.out.println("clent_name: "+Thread.currentThread().getName()+" server msg:" + msg.trim());
            }
          }
        }
      } while (1 == 1);
    }
  }
}


